/*
 * Unidata Platform
 * Copyright (c) 2013-2020, UNIDATA LLC, All rights reserved.
 *
 * Commercial License
 * This version of Unidata Platform is licensed commercially and is the appropriate option for the vast majority of use cases.
 *
 * Please see the Unidata Licensing page at: https://unidata-platform.com/license/
 * For clarification or additional options, please contact: info@unidata-platform.com
 * -------
 * Disclaimer:
 * -------
 * THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND
 * REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE
 * IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY,
 * FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND
 * THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING.
 */

package org.unidata.mdm.rest.meta.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HttpMethod;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;

import org.apache.commons.lang3.StringUtils;
import org.apache.cxf.jaxrs.ext.xml.ElementClass;
import org.springframework.beans.factory.annotation.Autowired;
import org.unidata.mdm.meta.type.search.ModelHeaderField;
import org.unidata.mdm.meta.type.search.ModelIndexType;
import org.unidata.mdm.rest.search.configuration.SearchRestConfigurationConstants;
import org.unidata.mdm.rest.search.converter.SearchResultToRestSearchResultConverter;
import org.unidata.mdm.rest.search.ro.SearchResultRO;
import org.unidata.mdm.rest.system.ro.ErrorResponse;
import org.unidata.mdm.rest.system.service.AbstractRestService;
import org.unidata.mdm.search.context.SearchRequestContext;
import org.unidata.mdm.search.service.SearchService;
import org.unidata.mdm.search.type.form.FieldsGroup;
import org.unidata.mdm.search.type.form.FormField;
import org.unidata.mdm.search.type.query.SearchQuery;
import org.unidata.mdm.search.util.SearchUtils;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;

/**
 * @author Mikhail Mikhailov on Mar 4, 2020
 */
@Path(MetaSearchRestService.SERVICE_PATH)
@Consumes({"application/json"})
@Produces({"application/json"})
public class MetaSearchRestService extends AbstractRestService {
    /**
     * This endpoint path.
     */
    public static final String SERVICE_PATH = "search";

    @Autowired
    private SearchService searchService;

    /**
     * Searches meta data.
     *
     * @param fields the fields
     * @param text   the text
     * @param count  the count
     * @return hits
     */
    @GET
    @ElementClass(response = SearchResultRO.class)
    @Operation(
        description = "Найти реестры и справочники, содержащие в заданных полях текст или вернуть все, если поисковая строка пустая.",
        method = HttpMethod.GET,
        responses = {
            @ApiResponse(content = @Content(schema = @Schema(implementation = SearchResultRO.class)), responseCode = "200"),
            @ApiResponse(content = @Content(schema = @Schema(implementation = ErrorResponse.class)), responseCode = "500")
        }
    )
    public Response searchMeta(
            @Parameter(in = ParameterIn.QUERY) @QueryParam(SearchRestConfigurationConstants.SEARCH_PARAM_FIELDS) String fields,
            @Parameter(in = ParameterIn.QUERY) @QueryParam(SearchRestConfigurationConstants.SEARCH_PARAM_TEXT) String text,
            @Parameter(in = ParameterIn.QUERY) @QueryParam(SearchRestConfigurationConstants.SEARCH_PARAM_PAGE) @DefaultValue(SearchRestConfigurationConstants.DEFAULT_PAGE_NUMBER_VALUE) int page,
            @Parameter(in = ParameterIn.QUERY) @QueryParam(SearchRestConfigurationConstants.SEARCH_PARAM_COUNT) @DefaultValue(SearchRestConfigurationConstants.DEFAULT_OBJ_COUNT_VALUE) int count) {

        boolean isAllFieldsSearch = StringUtils.isBlank(fields);
        boolean isAllFetchSearch = StringUtils.isBlank(text);

        //because in ES we store un-normalized data
        if (isAllFieldsSearch && isAllFetchSearch) {
            return notFound();
        }

        List<FieldsGroup> groupFormFields = new ArrayList<>();
        if (!isAllFieldsSearch) {
            List<String> searchFields = SearchUtils.getFields(fields);
            List<FormField> formFields = new ArrayList<>(searchFields.size());
            for (String field : searchFields) {
                FormField formField = FormField.exact(ModelHeaderField.ENTRY_TYPE, field);
                formFields.add(formField);
            }
            groupFormFields.add(FieldsGroup.or(formFields));
        }

        if (!isAllFetchSearch) {
            FormField textField = FormField.startsWith(ModelHeaderField.ENTRY_DESCRIPTION, text);
            FieldsGroup textGroup = FieldsGroup.and(textField);
            groupFormFields.add(textGroup);
        }

        List<String> returnFields = Arrays.stream(ModelHeaderField.values())
                .map(ModelHeaderField::getName)
                .collect(Collectors.toList());

        SearchRequestContext scb = SearchRequestContext.builder(ModelIndexType.MODEL, ModelIndexType.INDEX_NAME)
                .skipEtalonId(true)
                .query(SearchQuery.formQuery(groupFormFields))
                .returnFields(returnFields)
                .count(count)
                .page(page > 0 ? page - 1 : page)
                .totalCount(true)
                .source(false)
                .runExits(true)
                .build();

        return ok(SearchResultToRestSearchResultConverter.convert(searchService.search(scb)));
    }
}
